#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#include <winioctl.h>
#include <dbt.h>
#include <initguid.h>
#include "ecutil.h"

#define DFE_SUCCESS         0
#define DFE_NG_NO_DEVICE   -1
#define DFE_NG_PARAMETER   -2
#define DFE_NG_SET_REPORT  -3
#define DFE_NG_GET_REPORT  -4
#define DFE_NG_GET_DATA    -5
#define DFE_NG_OUT_EP4     -6
#define DFE_NG_IN_EP3      -7

#define KB_VID  0x048D
#define KB_PID  0xCE00

#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "hid_lib\\hid.lib")

extern "C"
{
    #include "hid_inc\hidsdi.h"
    #include "hid_inc\setupapi.h"
}

wchar_t MyDevicePathName[MAX_PATH] = { 0 };
wchar_t MyVendorPathName[MAX_PATH] = { 0 };

// Token: For Set feature
HANDLE m_Handle = NULL;
int feature_length = 0;

enum KBTYPE {
	MEF1=1,
	MEF2,
	FOURZONE
};

enum VENDOR {
	LITEON    = 4,
	EVERLIGHT = 8
};


int m_kbType = 0;  // 1: F1  2: F2  3: 4ZONE
int m_LedVendor = 0; // 4: LiteOn   8: EverLight
int m_ProjID = 0;

int input_length = 0;
int output_length = 0;

typedef struct _DEBUG_MESSAGE
{
	int iFindHID[10];
	int iUsbSendData[7];
	int iUsbGetData[7];
	int iSet_Report[10];
	int iGet_Report[10];
	int	ireturnStatus;

}DEBUG_MESSAGE, *pDEBUGMESSAGE;


pDEBUGMESSAGE DLL_DebugMessage = NULL;

#ifdef __cplusplus    // If used by C++ code,
extern "C" {          // we need to export the C interface
#endif

	HINSTANCE hInst;                                // current instance
	HHOOK kholl = NULL;

	BOOL USB_FindTheHID(int a_VendorID, int a_ProductID, int  *a_pErrorData)
	{
		HIDD_ATTRIBUTES						Attributes;
		SP_DEVICE_INTERFACE_DATA			devInfoData;
		BOOL								bLastDevice = FALSE;
		int									iMemberIndex = 0;
		LONG								lResult = 0;
		ULONG								ulLength = 0;
		PSP_DEVICE_INTERFACE_DETAIL_DATA	detailData;
		HANDLE								hDeviceHandle;
		HANDLE								hDevInfo;
		GUID								HidGuid;
		ULONG								Required;
		BOOL								MyDeviceDetected = FALSE;

		MyDevicePathName[0] = 0;	MyVendorPathName[0] = 0;

		detailData = NULL;			hDeviceHandle = NULL;
		feature_length = 0;			input_length = 0;			output_length = 0;

		HidD_GetHidGuid(&HidGuid);

		hDevInfo = SetupDiGetClassDevs(&HidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
		if (hDevInfo)
			a_pErrorData[0] = 0;
		else
			a_pErrorData[0] = 1;

		devInfoData.cbSize = sizeof(devInfoData);

		// iMemberIndex =  0;
		// Loop 100 Count
		for (iMemberIndex = 0; (iMemberIndex <= 100); iMemberIndex++) {
			if (MyDeviceDetected == FALSE) {
				if (SetupDiEnumDeviceInterfaces(hDevInfo, 0, &HidGuid, iMemberIndex, &devInfoData)) {
					lResult = SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, NULL, 0, &ulLength, NULL);

					detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(ulLength);
					detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

					//lResult = SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, detailData, ulLength, &Required, NULL);
					if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, detailData, ulLength, &Required, NULL)) {
						hDeviceHandle = CreateFile(detailData->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
							(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, NULL);

						//Get HID Attributes
						Attributes.Size = sizeof(Attributes);
						lResult = HidD_GetAttributes(hDeviceHandle, &Attributes);

						MyDeviceDetected = FALSE;
						if ((Attributes.VendorID == a_VendorID) && (Attributes.ProductID == a_ProductID)) {
							PHIDP_PREPARSED_DATA pPreData = NULL;
							if (HidD_GetPreparsedData(hDeviceHandle, &pPreData)) {
								HIDP_CAPS cap = { 0 };
								if (HidP_GetCaps(pPreData, &cap) == HIDP_STATUS_SUCCESS) {
									//printf("Usage: %#x  UsagePage: %#x\n", cap.Usage, cap.UsagePage);
									if (cap.Usage == 1 && (cap.UsagePage == 0xFF02 || cap.UsagePage == 0xFF03 || cap.UsagePage == 0xFF12))
									{
										wcscpy_s(MyDevicePathName, sizeof(MyDevicePathName), detailData->DevicePath);

										switch (cap.UsagePage)
										{
											case 0xFF02:
												m_kbType = MEF1;
												break;
											case 0xFF03:
												m_kbType = MEF2;
												break;
											case 0xFF12:
												m_kbType = FOURZONE;
												break;
										}

										if (cap.FeatureReportByteLength > 0)
										{
											printf("Now using this-> Usage: %#x  UsagePage: %#x\n", cap.Usage, cap.UsagePage);
											MyDeviceDetected = TRUE;
											input_length = cap.InputReportByteLength;
											feature_length = cap.FeatureReportByteLength;
											output_length = cap.OutputReportByteLength;

											m_Handle = hDeviceHandle;
											return true;
										}
									}
								}
								HidD_FreePreparsedData(pPreData);
							}
						}
					}
					CloseHandle(hDeviceHandle);
					free(detailData);
				}
				else {
					if (GetLastError() == ERROR_NO_MORE_ITEMS) {
						MessageBox(NULL, L"USB disconnect", L"TF_KB ERROR", MB_OK | MB_ICONSTOP);
						break;
					}
				}
			}
			else break;
		}
		SetupDiDestroyDeviceInfoList(hDevInfo);
		return MyDeviceDetected;
	}

	int writeFeature(UINT8 *buff)
	{
		Sleep(1); //delay....
		return HidD_SetFeature(m_Handle, buff, feature_length);
	}

	int getFeature(UINT8 *buff)
	{
		return HidD_GetFeature(m_Handle, buff, feature_length);
	}

	void setRowIndex16H(BYTE index)
	{
		BYTE array[9] = {0};
		array[1] = 22;
		array[3] = index;
		writeFeature(array);
		//Sleep(1000);
	}

	void detectKBLedVendor(void)
	{
		BYTE temp;
		InitWMI();
		WmiReadEcRam(0x0472, &temp);
		if ((temp & 0x8) == 0x8) {
			m_LedVendor = EVERLIGHT;
		} else if ((temp & 0x4) == 0x4) {
			m_LedVendor = LITEON;
		}
		printf("ME 2nd LED Vendor: %s\n", m_LedVendor == EVERLIGHT ? "EverLight" : "LiteOn");
	}

	int getProjectID(void)
	{
		BYTE temp = 0;
		InitWMI();
		WmiReadEcRam(0x0470, &temp);
		m_ProjID = temp;
		return m_ProjID;
	}

	void SetColorFourZone(UINT8 index, UINT8 R, UINT8 G, UINT8 B)
	{
		UINT8 array2[9] = { 0 };
		array2[1] = 20;
		array2[3] = index;
		array2[4] = R;
		array2[5] = G;
		array2[6] = B;
		if (!writeFeature(array2))
			printf("writeFeature failed\n");
	}

	void SetColor(UINT8 R, UINT8 G, UINT8 B)
	{
		UINT8 array3[9] = {0};
		UINT8 array2[9] = {0};
		UINT8 KBTYPE = 0;
		UINT8 R1=R, G1=G, B1=B;

		switch (m_kbType)
		{
			case MEF1:
				break;

			case MEF2:
				detectKBLedVendor();
				if (m_LedVendor == 4)
				{
					if (R == 255 && G == 255 && B == 255)
					{
						R1 = 0xff;
						G1 = 50;
						B1 = 170;
					}
					else if (R == 243 && G == 152 && B == 0)
					{
						R1 = 0xff;
						G1 = 19;
						B1 = 0;
					}
					else if (R == 255 && G == 241 && B == 0)
					{
						R1 = 0xff;
						G1 = 50;
						B1 = 0;
					}
					else if (R == 0 && G == 255 && B == 255)
					{
						R1 = 0;
						G1 = 50;
						B1 = 170;
					}
					else if (R == 138 && G == 0 && B == 255)
					{
						R1 = 138;
						G1 = 0;
						B1 = 170;
					}
					else if (R > 249 && G < 20 && B < 128)
					{
						if (B < 100)
						{
							R1 = R;
							G1 = G;
							B1 = B / 10;
						}
						else
						{
							R1 = R;
							G1 = G;
							B1 = B - 10;
						}
					}
					else if (R < 20 && B < G)
					{
						R1 = R;
						G1 = G;
						B1 = 170 * B / 0xff;
					}
					else if (G < 20 && R < B)
					{
						R1 = R;
						G1 = G;
						B1 = B;
					}
					else
					{
						R1 = R;
						G1 = 50 * G / 0xff;
						B1 = 170 * B / 0xff;
					}
				}
				else if (m_LedVendor == 8)
				{
					if (R == 255 && G == 255 && B == 255)
					{
						R1 = 0xff;
						G1 = 70;
						B1 = 80;
					}
					else if (R == 243 && G == 152 && B == 0)
					{
						R1 = 0xff;
						G1 = 25;
						B1 = 0;
					}
					else if (R == 241 && G == 90 && B == 36)
					{
						R1 = 0xff;
						G1 = 5;
						B1 = 0;
					}
					else if (R == 247 && G == 147 && B == 30)
					{
						R1 = 0xff;
						G1 = 15;
						B1 = 0;
					}
					else if (R == 255 && G == 241 && B == 0)
					{
						R1 = 0xff;
						G1 = 70;
						B1 = 0;
					}
					else if (R == 0 && G == 255 && B == 255)
					{
						R1 = 0;
						G1 = 70;
						B1 = 80;
					}
					else if (R == 138 && G == 0 && B == 255)
					{
						R1 = 138;
						G1 = 0;
						B1 = 80;
					}
					else if (R > 249 && G < 20 && B < 128)
					{
						if (B < 100)
						{
							R1 = R;
							G1 = G;
							B1 = B / 10;
						}
						else
						{
							R1 = R;
							G1 = G;
							B1 = B - 10;
						}
					}
					else if (R < 20 && B < G)
					{
						R1 = R;
						G1 = G;
						B1 = 80 * B / 0xff;
					}
					else if (G < 20 && R < B)
					{
						R1 = R;
						G1 = G;
						B1 = B;
					}
					else
					{
						R1 = R;
						G1 = 70 * G / 0xff;
						B1 = 80 * B / 0xff;
					}
				}
				else if (R == 255 && G == 255 && B == 255)
				{
					R1 = 0xff;
					G1 = 180;
					B1 = 200;
				}
				else if (R == 243 && G == 152 && B == 0)
				{
					R1 = 0xff;
					G1 = 42;
					B1 = 0;
				}
				else if (R == 241 && G == 90 && B == 36)
				{
					R1 = 0xff;
					G1 = 25;
					B1 = 0;
				}
				else if (R == 247 && G == 147 && B == 30)
				{
					R1 = 0xff;
					G1 = 42;
					B1 = 0;
				}
				else if (R == 255 && G == 241 && B == 0)
				{
					R1 = 0xff;
					G1 = 180;
					B1 = 0;
				}
				else if (R == 0 && G == 255 && B == 255)
				{
					R1 = 0;
					G1 = 180;
					B1 = 200;
				}
				else if (R == 138 && G == 0 && B == 255)
				{
					R1 = 138;
					G1 = 0;
					B1 = 200;
				}
				else if (R > 249 && G < 20 && B < 128)
				{
					if (B < 100)
					{
						R1 = R;
						G1 = G;
						B1 = B / 10;
					}
					else
					{
						R1 = R;
						G1 = G;
						B1 = B - 10;
					}
				}
				else if (R < 20 && B < G)
				{
					R1 = R;
					G1 = G;
					B1 = 200 * B / 0xff;
				}
				else if (G < 20 && R < B)
				{
					R1 = R;
					G1 = G;
					B1 = B;
				}
				else
				{
					R1 = R;
					G1 = 180 * G / 0xff;
					B1 = 200 * B / 0xff;
				}
				break;

			case FOURZONE:
				getProjectID();  // check project ID for if need cheating.
				if (m_ProjID != 6 && m_ProjID != 7)
				{
					break;
				}

				// cheating code
				if (R == 243 && G == 152 && B == 0)
				{
					R1 = 0xff;
					G1 = 60;
					B1 = 0;
				}
				else if (R == 255 && G == 241 && B == 0)
				{
					R1 = 0xff;
					G1 = 180;
					B1 = 0;
				}
				else
				{
					R1 = R;
					G1 = 180 * G / 0xff;
					B1 = B;
				}
				break;

			default:
				break;
		}

		array3[1] = 20;
		array3[4] = R1;
		array3[5] = G1;
		array3[6] = B1;

		for (int i = 1; i < 8; i++)
			array2[i] = array3[i];

		for (int i = 1; i < 8; i++)
		{
			array2[3] = i;
			if(!writeFeature(array2))
				printf("writeFeature failed\n");
		}

		array2[0] = 0;
		array2[1] = 8;
		array2[2] = 2;
		array2[3] = 3;
		array2[4] = 10;
		array2[5] = 50;
		array2[6] = 8;
		array2[7] = 1;
		array2[8] = 0;
		if (!writeFeature(array2))
			printf("writeFeature failed\n");
	}

	int USB_Set_Report(BYTE *data, int len, int  *a_pErrorData)
	{
		HANDLE DeviceHandle;
		BYTE Set_Data[32] = { 0 };
		ULONG Result;

		if (MyDevicePathName[0] == 0)
			return 1;

		DeviceHandle = CreateFile(MyDevicePathName,
			0,
			FILE_SHARE_WRITE,
			(LPSECURITY_ATTRIBUTES)NULL,
			OPEN_EXISTING,
			0,
			NULL);

		if (DeviceHandle == INVALID_HANDLE_VALUE)
			return 2;

		memcpy(Set_Data + 1, data, len);
		Result = HidD_SetFeature(DeviceHandle, Set_Data, feature_length);
		CloseHandle(DeviceHandle);

		if (Result == 0)
			return 3;

		return 0;
	}

	int USB_Out_EP4(BYTE *data, int len)
	{
		HANDLE DeviceHandle;
		BYTE EP4_Data[100] = { 0 };
		ULONG Result;
		DWORD block_size, wlen;
		int i;

		if (MyDevicePathName[0] == 0)
			return 1;

		DeviceHandle = CreateFile(MyDevicePathName,
			GENERIC_WRITE,
			0,
			(LPSECURITY_ATTRIBUTES)NULL,
			OPEN_EXISTING,
			0,
			NULL);

		if (DeviceHandle == INVALID_HANDLE_VALUE)
			return 2;

		for (i = 0; i < len; i += 64)
		{
			memset(EP4_Data, 0, sizeof(EP4_Data));
			block_size = len - i;
			if (block_size > 64)
				block_size = 64;
			memcpy(EP4_Data + 1, data + i, block_size);

			wlen = 0;
			Result = WriteFile(DeviceHandle, EP4_Data, output_length, &wlen, 0);
			if ((Result == FALSE) || (wlen != output_length))
			{
				CloseHandle(DeviceHandle);
				return 3;
			}
		}

		CloseHandle(DeviceHandle);

		return 0;
	}

	int USB_Get_Report(BYTE *data, int len, int  *a_ipErrorData)
	{
		HANDLE DeviceHandle;
		BYTE Get_Data[32] = { 0 };
		ULONG Result;

		if (MyDevicePathName[0] == 0)
			return 1;

		DeviceHandle = CreateFile(MyDevicePathName,
			0,
			FILE_SHARE_READ | FILE_SHARE_WRITE,
			(LPSECURITY_ATTRIBUTES)NULL,
			OPEN_EXISTING,
			0,
			NULL);

		if (DeviceHandle == INVALID_HANDLE_VALUE)
			return 2;

		Result = HidD_GetFeature(DeviceHandle, Get_Data, feature_length);
		memcpy(data, Get_Data + 1, len);
		CloseHandle(DeviceHandle);

		if (Result == 0)
			return 3;

		return 0;
	}

	int USB_In_EP3(BYTE *data, int len)
	{
		HANDLE DeviceHandle;
		BYTE EP3_Data[100] = { 0 };
		ULONG Result;
		DWORD block_size, rlen;
		int i;

		if (MyDevicePathName[0] == 0)
			return 1;

		DeviceHandle = CreateFile(MyDevicePathName,
			GENERIC_READ,
			0,
			(LPSECURITY_ATTRIBUTES)NULL,
			OPEN_EXISTING,
			0,
			NULL);

		if (DeviceHandle == INVALID_HANDLE_VALUE)
			return 2;

		for (i = 0; i < len; i += 64)
		{
			memset(EP3_Data, 0, sizeof(EP3_Data));
			block_size = len - i;
			if (block_size > 64)
				block_size = 64;

			rlen = 0;
			Result = ReadFile(DeviceHandle, EP3_Data, input_length, &rlen, 0);
			if ((Result == FALSE) || (rlen != input_length))
			{
				CloseHandle(DeviceHandle);
				return 3;
			}

			memcpy(data + i, EP3_Data + 1, block_size);
		}

		CloseHandle(DeviceHandle);

		return 0;
	}

	BYTE Gaming_checksum(BYTE *data, int len)
	{
		int i, sum = 0;

		for (i = 0; i < len; i++)
			sum += data[i];

		return (0xFF - (sum & 0xFF));
	}

	int GetFirmwareVersion()
	{
		BYTE array[9] = {0};
		BYTE FW[9] = {0};

		array[1] = 128;
		if (writeFeature(array)) {
			if(getFeature(FW)){
				printf("%X.%X.%X.%X\n", FW[2], FW[3], FW[4], FW[5]);
			}
		}
		else {
			printf("writeFeature failed");
			return 0;
		}
		return 1;
	}

	int InitUSBHid()
	{
		int  RET;
		return USB_FindTheHID(KB_VID, KB_PID, &RET);
	}

	DEBUG_MESSAGE TF_Get_firmware_version (void)
	{
		BYTE set_data[8] = {0x80};
		BYTE get_data[8] = {0};
	
		if(DLL_DebugMessage) {
			delete DLL_DebugMessage;
			DLL_DebugMessage = NULL;
		}
		DLL_DebugMessage = new DEBUG_MESSAGE;

		set_data[7] = Gaming_checksum(set_data, 7);
		DLL_DebugMessage->iUsbSendData[0] = set_data[0];
		DLL_DebugMessage->iUsbSendData[1] = set_data[1];
		DLL_DebugMessage->iUsbSendData[2] = set_data[2];
		DLL_DebugMessage->iUsbSendData[3] = set_data[3];
		DLL_DebugMessage->iUsbSendData[4] = set_data[4];
		DLL_DebugMessage->iUsbSendData[5] = set_data[5];
		DLL_DebugMessage->iUsbSendData[6] = set_data[6];
		DLL_DebugMessage->iUsbSendData[7] = set_data[7];
		DLL_DebugMessage->iUsbGetData[0] = 0;
		DLL_DebugMessage->iUsbGetData[1] = 0;
		DLL_DebugMessage->iUsbGetData[2] = 0;
		DLL_DebugMessage->iUsbGetData[3] = 0;
		DLL_DebugMessage->iUsbGetData[4] = 0;
		DLL_DebugMessage->iUsbGetData[5] = 0;
		DLL_DebugMessage->iUsbGetData[6] = 0;
		DLL_DebugMessage->iUsbGetData[7] = 0;

		if ( ! USB_FindTheHID(KB_VID, KB_PID, DLL_DebugMessage->iFindHID) ){
			//return DFE_NG_NO_DEVICE;
			DLL_DebugMessage->ireturnStatus = DFE_NG_NO_DEVICE;
			return *DLL_DebugMessage;
		}

		if ( USB_Set_Report(set_data, 8, DLL_DebugMessage->iSet_Report) ){
			//return DFE_NG_SET_REPORT;
			DLL_DebugMessage->ireturnStatus = DFE_NG_NO_DEVICE;
			return *DLL_DebugMessage;
		}

		if ( USB_Get_Report(get_data, 8, DLL_DebugMessage->iGet_Report) ){
			//return DFE_NG_GET_REPORT;
			DLL_DebugMessage->ireturnStatus = DFE_NG_GET_REPORT;
			return *DLL_DebugMessage;
		}

		if ( get_data[0] != 0x80 ){
			//return DFE_NG_GET_DATA;
			DLL_DebugMessage->ireturnStatus = DFE_NG_GET_DATA;
			return *DLL_DebugMessage;
		}

		DLL_DebugMessage->iUsbGetData[0] = get_data[0];
		DLL_DebugMessage->iUsbGetData[1] = get_data[1];
		DLL_DebugMessage->iUsbGetData[2] = get_data[2];
		DLL_DebugMessage->iUsbGetData[3] = get_data[3];
		DLL_DebugMessage->iUsbGetData[4] = get_data[4];
		DLL_DebugMessage->iUsbGetData[5] = get_data[5];
		DLL_DebugMessage->iUsbGetData[6] = get_data[6];
		DLL_DebugMessage->iUsbGetData[7] = get_data[7];
		DLL_DebugMessage->ireturnStatus = DFE_SUCCESS;
		//return ((get_data[1]<<8)|get_data[2]);
		return *DLL_DebugMessage;
	}


	int Set_Test_Mode(int imode, int ispeed)
	{
		BYTE set_data[8] = {0x20};
		if(DLL_DebugMessage) {
			delete DLL_DebugMessage;
			DLL_DebugMessage = NULL;
		}
		DLL_DebugMessage = new DEBUG_MESSAGE;

		if(!USB_FindTheHID(KB_VID, KB_PID, DLL_DebugMessage->iFindHID)) {
			return DFE_NG_NO_DEVICE;
		}

		if((imode == 0) || (ispeed > 0x0A))
			return DFE_NG_PARAMETER;
		set_data[1] = imode;
		set_data[2] = ispeed;
		if(USB_Set_Report(set_data, 8, DLL_DebugMessage->iSet_Report))
			return DFE_NG_SET_REPORT;

		return DFE_SUCCESS;
	}

#ifdef __cplusplus
}
#endif
